package gui.commands;

import data.DataVector;

import java.util.Iterator;
import java.util.LinkedList;

/**
 * User: Szymon Kudzia
 * Date: 10.05.13
 * Time: 12:40
 * Pattern: StateMachine, Singleton
 */
public class CommandManager {
	protected static final int HISTORY_COUNT = 20;
	protected static CommandManager commandManager = new CommandManager();
	
	protected RealCommandManager classDiagramCommandManager = new RealCommandManager();
	protected RealCommandManager stateDiagramCommandManager = new RealCommandManager();
	
	protected CommandManager() {}

	public static CommandManager getInstance() {
		return commandManager;
	}

	public void clearHistory() {
		classDiagramCommandManager = new RealCommandManager();
		stateDiagramCommandManager = new RealCommandManager();
	}

	public boolean canUndoClassDragramCommand() {
		return !(classDiagramCommandManager.state instanceof RealCommandManager.NoUndoRedoState
				||
				classDiagramCommandManager.state instanceof RealCommandManager.NoUndoState);
	}

	public void undoClassDiagramCommand() {
		classDiagramCommandManager.undo();

		DataVector.getInstance().refresh();
	}

	public boolean canRedoClassDragramCommand() {
		return !(classDiagramCommandManager.state instanceof RealCommandManager.NoUndoRedoState
				||
				classDiagramCommandManager.state instanceof RealCommandManager.NoRedoState);
	}

	public void redoClassDiagramCommand() {
		classDiagramCommandManager.redo();

		DataVector.getInstance().refresh();
	}

	public void addAndExecuteClassDiagramCommand(ICommand command) {
		classDiagramCommandManager.addAndExecuteCommand(command);

		DataVector.getInstance().refresh();
	}

	public boolean canUndoStateDragramCommand() {
		return !(stateDiagramCommandManager.state instanceof RealCommandManager.NoUndoRedoState
				||
				stateDiagramCommandManager.state instanceof RealCommandManager.NoUndoState);
	}

	public void undoStateDiagramCommand() {
		stateDiagramCommandManager.undo();

		DataVector.getInstance().refresh();
	}


	public boolean canRedoStateDragramCommand() {
		return !(stateDiagramCommandManager.state instanceof RealCommandManager.NoUndoRedoState
				||
				stateDiagramCommandManager.state instanceof RealCommandManager.NoRedoState);
	}

	public void redoStateDiagramCommand() {
		stateDiagramCommandManager.redo();

		DataVector.getInstance().refresh();
	}

	public void addAndExecuteStateDiagramCommand(ICommand command) {
		stateDiagramCommandManager.addAndExecuteCommand(command);

		DataVector.getInstance().refresh();
	}
	
	
	protected class RealCommandManager {
		protected State state = new NoUndoRedoState();
	    
		protected LinkedList<ICommand> commands = new LinkedList<ICommand>();
		protected int currentCommand = 0;
	
		public void undo() {
			state.undo();
		}
	
		public void redo() {
			state.redo();
		}
	
		public void addAndExecuteCommand(ICommand command) {
			putUndoCommandsBelowNewOne();
	
			command.execute();
			state.add(command);
	
			removeOldCommand();
		}
		
		protected void removeOldCommand() {
			if (commands.size() > HISTORY_COUNT)
				commands.removeFirst();
		}
	
		protected void putUndoCommandsBelowNewOne() {
			LinkedList<ICommand> pom = new LinkedList<ICommand>();
	
			while (currentCommand != commands.size()) {
				pom.add(commands.pollLast());
			}
	
			commands.addAll(pom);
		}
	
		protected void setState(State state) {
			this.state = state;
		}
	
		protected class State {
			void undo() {
				commands.get(currentCommand--).undo();
	
				if (currentCommand == -1) {
					setState(new NoUndoState());
					currentCommand = 0;
				}
				else if (currentCommand > 0)
					setState(new State());
			}
	
			void redo() {
				commands.get(currentCommand++).redo();
	
				if (currentCommand == commands.size()) {
					setState(new NoRedoState());
					--currentCommand;
				}
				else if (currentCommand > 0)
					setState(new State());
			}
	
			void add(ICommand command) {
				commands.add(command);
	
				currentCommand = commands.size() - 1;
	
				if (commands.size() >= 1)
					setState(new NoRedoState());
			}
		}
	
		protected class NoUndoState extends State {
			@Override
			void undo() {
			}
	
			@Override
			void redo() {
				super.redo();
			}
		}
	
		protected class NoRedoState extends State {
			@Override
			void undo() {
				super.undo();
			}
	
			@Override
			void redo() {
			}
		}
	
		protected class NoUndoRedoState extends State {
			@Override
			void undo() {
			}
	
			@Override
			void redo() {
			}
		}
	}
}
